package com.atguigu.gmall.activity.service.impl;

import com.atguigu.gmall.activity.model.OrderRecode;
import com.atguigu.gmall.activity.model.SeckillGoods;
import com.atguigu.gmall.activity.mapper.SeckillGoodsMapper;
import com.atguigu.gmall.activity.model.UserRecode;
import com.atguigu.gmall.activity.service.SeckillGoodsService;
import com.atguigu.gmall.activity.util.CacheHelper;
import com.atguigu.gmall.common.constant.MqConst;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.common.service.RabbitService;
import com.atguigu.gmall.common.util.DateUtil;
import com.atguigu.gmall.common.util.MD5;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.benmanes.caffeine.cache.Cache;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.BoundListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 *  业务实现类
 * @author atguigu
 * @since 2023-05-13
 */
@SuppressWarnings("all")
@Service
public class SeckillGoodsServiceImpl extends ServiceImpl<SeckillGoodsMapper, SeckillGoods> implements SeckillGoodsService {
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private Cache<String,String> seckillCache;
    @Autowired
    private RabbitService rabbitService;

    /**
     * 查询Redis中当日秒杀商品列表
     * @return
     */
    @Override
    public List<SeckillGoods> getSeckillGoodsList() {
        String seckillGoodsKey = RedisConst.SECKILL_GOODS;
        BoundHashOperations<String,String,SeckillGoods> hashOps = redisTemplate.boundHashOps(seckillGoodsKey);
        return hashOps.values();
    }

    /**
     * 查询指定秒杀商品信息
     * @param skuId 商品id
     * @return 查询到指定秒杀商品信息结果
     */
    @Override
    public SeckillGoods getSeckillGoods(Long skuId) {
        String seckillGoods = RedisConst.SECKILL_GOODS;
        BoundHashOperations<String,String,SeckillGoods> hashOps = redisTemplate.boundHashOps(seckillGoods);
        if(hashOps.hasKey(skuId.toString())){
            return hashOps.get(skuId.toString());
        }
        return null;
    }

    /**
     * 点击秒杀按钮前,为当前登录用户生成指定商品抢购码,避免使用非法手段抢购
     * @param userId
     * @param skuId
     * @return
     */
    @Override
    public String getSeckillSkuIdStrBuyCode(String userId, Long skuId) {
        //1.验证本地缓存中商品状态是否为:"1"
        String state = seckillCache.getIfPresent(skuId.toString());
        if(StringUtils.isNotBlank(state)){
            throw new RuntimeException(ResultCodeEnum.ILLEGAL_REQUEST.getMessage()); //非法请求
        }
        if("0".equals(state)){
            throw new RuntimeException(ResultCodeEnum.SECKILL_END.getMessage()); //秒杀已结束
        }

        //2.验证该商品是否在销售时间内
        if("1".equals(state)){
            String seckillGoodsKey = RedisConst.SECKILL_GOODS;
            BoundHashOperations<String,String,SeckillGoods> hashOps = redisTemplate.boundHashOps(seckillGoodsKey);
            SeckillGoods seckillGoods = hashOps.get(skuId.toString());
            Date now = new Date();
            if(seckillGoods != null && DateUtil.dateCompare(seckillGoods.getStartTime(),now) && DateUtil.dateCompare(now,seckillGoods.getEndTime())){

                //3.为当前登录用户生成抢购码, todo 存入redis便于验证
                String buyCode = MD5.encrypt(userId + skuId);
                return buyCode;
            }
        }
        throw new RuntimeException(ResultCodeEnum.SECKILL_ILLEGAL.getMessage()); //请求不合法
    }

    /**
     * 秒杀入队-将用户购买意向发送到消息队列
     * @param skuId 商品id
     * @param buyCode 抢购码
     * @return 返回消息队列消息结果
     */
    @Override
    public Result seckillResultToQueue(String userId, Long skuId, String userBuyCode) {
        //1.验证用户提交抢购码是否正确
        String buyCode = MD5.encrypt(userId + skuId);
        if(!userBuyCode.equals(buyCode)){
            return Result.build(null, ResultCodeEnum.ILLEGAL_REQUEST);  //非法请求
        }
        //2.验证本地缓存中商品状态
        String status = seckillCache.getIfPresent(skuId.toString());
        if("0".equals(status) || StringUtils.isNotBlank(status)){
            return Result.build(null, ResultCodeEnum.SECKILL_FINISH);   //已售罄
        }
        //3.构建用户秒杀商品购买意向发送到消息队列
        if("1".equals(status)){
            //3.1封装用户购买意向对象(包含用户id,商品id,购买数量)
            UserRecode userRecode = new UserRecode();
            userRecode.setUserId(userId);
            userRecode.setSkuId(skuId);
            //3.2发送消息到消息队列中
            rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_SECKILL_USER, MqConst.ROUTING_SECKILL_USER, userRecode);
            return Result.ok();
        }
        return null;
    }

    /**
     * 预下单接口[监听秒杀队列中用户购买意向,完成核心秒杀业务-获取秒杀资格]
     * @param userRecode
     */
    @Override
    public void processSeckillRequest(UserRecode userRecode) {
        //1.从本地缓存获取订单状态     1:执行 0:结束
        String status = seckillCache.getIfPresent(userRecode.getSkuId().toString());
        if(StringUtils.isNotBlank(status) || "0".equals(status)){
            return;
        }
        //2.判断用户是否重复排队
        String ifReQuestKey = RedisConst.SECKILL_USER + userRecode.getUserId() + ":" + userRecode.getSkuId() + "request";
        Boolean ifRequest = redisTemplate.opsForValue().setIfPresent(ifReQuestKey, userRecode.getSkuId().toString(), 1, TimeUnit.HOURS);
        if(!ifRequest){
            return;
        }
        //3.尝试从Redis中商品库存List结构中弹出商品数据
        String seckillStockKey = RedisConst.SECKILL_STOCK_PREFIX + userRecode.getSkuId();
        BoundListOperations<String,String> listOps = redisTemplate.boundListOps(seckillStockKey);
        String hashStock = listOps.rightPop();
        //3.1失败:更新所有节点本地缓存
        if(StringUtils.isBlank(hashStock)){
            redisTemplate.convertAndSend("seckillpush",userRecode.getSkuId() + "0");
            return;
        }
        //3.2获取数据成功获取秒杀资格
        //4.为用户生成临时订单,存入Redis
        OrderRecode tempOrder = new OrderRecode();
        tempOrder.setUserId(userRecode.getUserId());
        tempOrder.setNum(1);
        //生成临时订单的标识
        String orderNoStr = UUID.randomUUID().toString().replace("-", "");
        tempOrder.setOrderStr(orderNoStr);
        tempOrder.setSeckillGoods(this.getSeckillGoods(userRecode.getSkuId()));
        String tempOrderKey = RedisConst.SECKILL_ORDERS;
        String hashKey = userRecode.getUserId() + ":" + userRecode.getSkuId();
        redisTemplate.opsForHash().put(tempOrderKey, hashKey, tempOrder);
        //5.发消息到MQ更新库存数量
        rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_WARE_STOCK, MqConst.ROUTING_WARE_STOCK, userRecode.getSkuId());
    }

    /**
     * 扣减秒杀商品库存
     * @param skuId
     */
    @Override
    public void getDeductSeckillStock(Long skuId) {
        //1.跟新Redis中秒杀商品?Hash结构中库存数量
        //1.1查新Redis中商品库存List结构中最新库存数
        String stockListKey = RedisConst.SECKILL_STOCK_PREFIX + skuId;
        BoundListOperations<String,String> listOps = redisTemplate.boundListOps(stockListKey);
        Long stockCount = listOps.size();
        //1.2查询Redis中秒杀商品信息更新数量
        String seckillGoodsKey = RedisConst.SECKILL_GOODS;
        BoundHashOperations<String,String,SeckillGoods> hashOps = redisTemplate.boundHashOps(seckillGoodsKey);
        SeckillGoods seckillGoods = hashOps.get(skuId.toString());
        if(seckillGoodsKey != null){
            seckillGoods.setStockCount(stockCount.intValue());
            hashOps.put(skuId.toString(), seckillGoods);
            //2.更新MySql数据库中秒杀库存数量
            this.updateById(seckillGoods);
        }
    }

    /**
     * 前端定时发起请求检查当前用户商品秒杀结果
     * @param request
     * @param skuId 秒杀商品id
     * @return
     */
    @Override
    public Result checkOrderResult(String userId, Long skuId) {
        //查询用户对秒杀商品秒杀结果状态,根据业务响应不同状态码,给出响应提示
        //1.判断用户是否在排队,有机会秒杀成功
        String ifQueueKey = RedisConst.SECKILL_USER + userId + ":" + skuId;
        Boolean ifQueue = redisTemplate.hasKey(ifQueueKey);
        if(ifQueue){
            //2.判断用户是否提交了订单-218
            String orderKey = RedisConst.SECKILL_ORDERS_USERS;
            BoundHashOperations<String,String,Long> orderHashOps = redisTemplate.boundHashOps(orderKey);
            if(orderHashOps.hasKey(userId)){
                //用户提交秒杀订单,获取秒杀成功后订单id,将订单id返回
                Long orderId = orderHashOps.get(userId);
                return Result.build(orderId, ResultCodeEnum.SECKILL_ORDER_SUCCESS);
            }
            //3.判断用户是否产生了临时订单
            String tempOrderKey = RedisConst.SECKILL_ORDERS;
            BoundHashOperations<String,String,OrderRecode> tempOrderHashOps = redisTemplate.boundHashOps(tempOrderKey);
            if(tempOrderHashOps.hasKey(userId)){
                //用户抢单成功,获得秒杀下单资格,将临时订单数据返回
                OrderRecode orderRecode = tempOrderHashOps.get(userId);
                return Result.build(orderRecode, ResultCodeEnum.SECKILL_SUCCESS);
            }

        }
        //4.判断本地缓存中商品状态:0售罄-211
        String state = (String) CacheHelper.get(skuId.toString());
        if("0".equals(state)){
            return Result.build(null, ResultCodeEnum.SECKILL_FINISH);
        }

        //5.其他情况-正在排队中
        return Result.build(null, ResultCodeEnum.SECKILL_RUN);
    }
}